
use std::{fs::{create_dir_all, remove_dir_all, remove_file}, io::Read, path::PathBuf, sync::mpsc::{Sender, channel}, thread::spawn};

use clap::Args;
use serde::{Deserialize, Serialize};
use wtask_base::{app::get_wtask_data_dir, cmd::{create_file, file_cp, file_to_x_power, file_unzip}, error::{WError, WResult}, export::{Local, log_debug, log_info}, logs::{TIME_FORMAT_FULL, log_format}, tcp::{WTcpStream, stream_read_data, stream_write_data, stream_write_raw}};

use crate::{
    basic::WPath,
    file::{upload::AppFileUpload, utils::FileUDArgs},
    task::utils::{
        func::task_local_simple_check,
        meta::{CmdMeta, TaskAddMeta, TaskList, TaskMeta, WTASK_DISABLED_FILE, WTASK_INSTALL_FILES, WTASK_SOURCE_FILE}
    },
    utils::{AppTrait, StreamStatus}
};








#[derive(Debug, Clone, Serialize, Deserialize, Args)]
pub struct AppTaskAdd {
    #[command(flatten)]
    pub data: TaskAddMeta,

    /// 是否是所有任务的备份
    #[arg(long)]
    pub archv: bool,
}


impl AppTaskAdd {
    /// 安装程序，返回安装后的task_meta, 和安装程序及检查的log_receiver
    fn install(file_task_add: PathBuf, wait: bool, tx_log: Sender<String>) -> WResult<TaskMeta> {
        let dir_task = get_wtask_data_dir("task", false)?;
        let task_id = file_task_add.file_name().unwrap().to_string_lossy().replace(".zip", "").to_string();
        let dir_install = dir_task.join(&task_id);
        let file_exe = dir_install.join(WTASK_INSTALL_FILES[2]);
        let file_install = dir_install.join(WTASK_INSTALL_FILES[3]);
        let file_disabled = dir_install.join(WTASK_DISABLED_FILE);

        let dir_install_clone = dir_install.clone();
        let tx_log_clone = tx_log.clone();
        // 解压，解压后就能读取task meta了
        create_dir_all(&dir_install)?;
        create_file(&file_disabled, &format!("{} Installing", Local::now().format(TIME_FORMAT_FULL)))?;
        file_unzip(&file_task_add, &dir_install)?;
        let func_install = move || {
            // 源码压缩包移动
            file_cp(&file_task_add, &dir_install.join(WTASK_SOURCE_FILE))?;

            log_info!("Task add: {task_id}");
            let file_install_str = file_install.to_string_lossy().to_string();

            // 运行权限
            file_to_x_power(&file_install)?;
            file_to_x_power(&file_exe)?;
            
            let (task_name, task_version) = task_id
                .rsplit_once(".")
                .ok_or(WError::Empty)?;

            
            let meta = CmdMeta::new(
                task_id.clone(),
                "任务安装".to_owned(),
                None,
                Some(format!("wtask_{}:{}", task_name.to_lowercase(), task_version)),
                format!("task install: {task_name}@{task_version}"),
                format!("{}@{}", task_name, task_version),
                vec![file_install_str],
                dir_install.clone(),
                None
            );
            meta.save_to_file()?;

            let tx_log1 = tx_log.clone();
            let msg_func1 = Box::new(move | _, v | {
                let _ = tx_log1.send(log_format(v));
            });

            let r = meta.run_cmd_task(msg_func1)?;

            // 等待安装
            r.handle.join().map_err(|_| WError::Join)??;
            
            // 检查
            let msg_func = move | _: bool, v: String | {
                let _ = tx_log.send(log_format(v));
            };
            let is_err = task_local_simple_check(&dir_install, msg_func)?;

            // 成功的话删除禁用状态
            if !is_err {
                let _ = remove_file(file_disabled);
            }
            Ok::<_, WError>(())
        };
        
        if wait {
            if let Err(e) = func_install() {
                let _ = tx_log_clone.send(log_format(format!("task install error: {}", e)));
            }
        } else {
            spawn(move || {
                if let Err(e) = func_install() {
                    let _ = tx_log_clone.send(log_format(format!("task install error: {}", e)));
                }
            });
        };

        let res = TaskMeta::new(&dir_install_clone)?;
        Ok(res)
    }
}




impl AppTrait for AppTaskAdd {
    type Status = (usize, Box<dyn Read + Send>);
    type Res = TaskList;

    async fn client_handle<M: FnMut(bool, String) + Send + Sync>(self, stream: &WTcpStream, key: &str, mut msg_func: M, status: Option<Self::Status>) -> WResult<Self::Res> {
        let file_temp = self.data.zip_task_files(&mut msg_func)?;
        stream_write_data(&WPath::from(&file_temp), stream.1.as_ref(), key).await?;
        if self.data.path.ends_with(".zip") {
            // 直接请求的就是文件
        } else {
            // 客户端压缩的
            // upload file_temp
            let t = FileUDArgs::new(file_temp);
            let task_upload = AppFileUpload::from(t);
            let file_temp = task_upload.client_handle(stream, key, &mut msg_func, status).await?;
            stream_write_data(&file_temp, stream.1.as_ref(), key).await?;
        };

        let task_list: TaskList = Self::client_wait_log(
            stream,
            key,
            &mut msg_func
        ).await?;
        for task_single in &task_list.apps {
            msg_func(false, format!("Task Add: {}, {}", task_single.id, task_single.version));
        }
        Ok(task_list)
    }

    async fn server_handle(self, server_token: &str, session_token: &str, stream_session: &WTcpStream, stream_status: StreamStatus) -> WResult<()> {
        let file_task_add: WPath = stream_read_data(stream_session.0.as_ref(), session_token).await?;
        let file_task_add: PathBuf = file_task_add.into();
        let file_task_add: PathBuf = if self.data.path.ends_with(".zip") {
            // 直接请求的就是文件
            file_task_add
        } else {
            // 客户端压缩的
            let t = FileUDArgs::new(file_task_add);
            let task_upload = AppFileUpload::from(t);
            task_upload.server_handle(server_token, session_token, stream_session, stream_status).await?;
            let file_task_add: WPath = stream_read_data(stream_session.0.as_ref(), session_token).await?;
            file_task_add.into()
        };
        log_debug!("Task add: {self:?}");
        let (tx_log, rx_log) = channel::<String>();
        let task_list = if self.archv {
            // unzip
            let dir_add = get_wtask_data_dir("temp", true)?;
            file_unzip(&file_task_add, &dir_add)?;
            let file_task = dir_add.read_dir()?.map(| v |{
                v.unwrap().path()
            })
            .filter(| v | v.ends_with(".zip"))
            .collect::<Vec<_>>();
            let mut task_list = Vec::with_capacity(file_task.len());
            for task_singe in file_task {
                let task_temp = Self::install(task_singe, !self.data.no_wait, tx_log.clone())?;
                task_list.push(task_temp);
            }
            remove_dir_all(dir_add)?;
            task_list
        } else {
            let task_temp = Self::install(file_task_add, !self.data.no_wait, tx_log)?;
            vec![task_temp]
        };
        let task_list = TaskList { apps: task_list, meta: false };
        stream_write_data(&task_list, stream_session.1.as_ref(), session_token).await?;
        if !self.data.no_wait {
            while let Ok(log_temp) = rx_log.recv() {
                stream_write_raw(stream_session.1.as_ref(), log_temp.as_bytes(), session_token).await?;
            }
            stream_write_raw(stream_session.1.as_ref(), format!("Install complete, {}", task_list).as_bytes(), session_token).await?;
        }
        Ok(())
    }
}

